package application;

import de.mxro.process.ProcessListener;
import de.mxro.process.Spawn;

import java.io.*;
import java.util.Locale;

/**
 * Created by vps on 2016/5/9 0009.
 */
public class Amr2Wav {
    /**
     * Determines the MIME-type of the resource represented by the input stream
     * {@code is} by reading its first few characters.
     *
     * @param is the resource representing input stream to determine the
     *           content type.
     * @return the guessed content type or {@code null} if the type could not be
     * determined.
     * @throws IOException if an I/O error occurs while reading from the input stream.
     */
    public static String guessContentTypeFromStream(InputStream is) throws IOException {
        if (!is.markSupported()) {
            return null;
        }
        // Look ahead up to 64 bytes for the longest encoded header
        is.mark(64);
        byte[] bytes = new byte[64];
        int length = is.read(bytes);
        is.reset();

        // If there is no data from the input stream, we can't determine content
        // type.
        if (length == -1) {
            return null;
        }

        // Check for Unicode BOM encoding indicators
        String encoding = "US-ASCII";
        int start = 0;
        if (length > 1) {
            if ((bytes[0] == (byte) 0xFF) && (bytes[1] == (byte) 0xFE)) {
                encoding = "UTF-16LE";
                start = 2;
                length -= length & 1;
            }
            if ((bytes[0] == (byte) 0xFE) && (bytes[1] == (byte) 0xFF)) {
                encoding = "UTF-16BE";
                start = 2;
                length -= length & 1;
            }
            if (length > 2) {
                if ((bytes[0] == (byte) 0xEF) && (bytes[1] == (byte) 0xBB) && (bytes[2] == (byte) 0xBF)) {
                    encoding = "UTF-8";
                    start = 3;
                }
                if (length > 3) {
                    if ((bytes[0] == (byte) 0x00) && (bytes[1] == (byte) 0x00) && (bytes[2] == (byte) 0xFE)
                            && (bytes[3] == (byte) 0xFF)) {
                        encoding = "UTF-32BE";
                        start = 4;
                        length -= length & 3;
                    }
                    if ((bytes[0] == (byte) 0xFF) && (bytes[1] == (byte) 0xFE) && (bytes[2] == (byte) 0x00)
                            && (bytes[3] == (byte) 0x00)) {
                        encoding = "UTF-32LE";
                        start = 4;
                        length -= length & 3;
                    }
                    // begin
                    if ((bytes[0] == (byte) 0xFF) && (bytes[1] == (byte) 0xD8) && (bytes[2] == (byte) 0xFF)
                            && (bytes[3] == (byte) 0xE1)) {
                        return "image/jpeg";

                    }
                    if (bytes[0] == 'G' && bytes[1] == 'I' && bytes[2] == 'F' && bytes[3] == '8') {
                        return "image/gif";

                    }
                    if (bytes[0] == '#' && bytes[1] == 'd' && bytes[2] == 'e' && bytes[3] == 'f') {
                        return "image/x-bitmap";

                    }
                    // end
                }
            }
        }
        if (length > 4) {
            int box_length = -1;
            try {

                DataInputStream dataInputStream = new DataInputStream(new ByteArrayInputStream(bytes));
                box_length = dataInputStream.readInt();
                dataInputStream.close();
                if (box_length != -1 && box_length > 16 && box_length <= 32) {
                    // 1c 28 18 24
                    // 4 4 4 4 12
                    // System.out.println("box_length:" + box_length);

                    // boxtype（4字节）：“ftyp”的ASCII码，box的标识
                    String box_type = new String(bytes, 4, 4, encoding);

                    // System.out.println(box_type);
                    // major_brand（4字节）：0x69736f6d：“isom“的ASCII码；
                    String major_brand = new String(bytes, 8, 4, encoding);
                    // mp41, mp42 .mp4 video/mp4, audio/mp4, application/mp4
                    // various, e.g. 3gp4, 3gp5 .3gp video/3gpp, audio/3gpp
                    // various, e.g. 3gp4, 3gp5 .3gp video/3gpp, audio/3gpp
                    // mjp2 .mj2 video/mj2
                    // "qt" .mov video/quicktime
                    // System.out.println(major_brand);
                    // minor_version（4字节）：0x00000200：ismo的版本号；
                    // String minor_version = new String(bytes, 12, 4, encoding);
                    byte ch1 = bytes[12];
                    byte ch2 = bytes[13];
                    byte ch3 = bytes[14];
                    byte ch4 = bytes[15];


                    if ((ch1 | ch2 | ch3 | ch4) < 0) {
                        throw new EOFException();
                    }
                    int minor_version = ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
                    // System.out.println(minor_version);
                    // compatible_brands（12字节）：说明本文件遵从（或称兼容）ismo,iso2,mp41三种协议。
                    String compatible_brands = new String(bytes, 16, 4, encoding);
                    //  System.out.println(compatible_brands);
                    if (box_type != null && box_type.contains("ftyp")) {
                        return "video/mp4";
                    }
                }
            } catch (Exception ex) {
                System.out.println("ex" + ex.getStackTrace());
            }

        }
        String header = new String(bytes, start, length - start, encoding);
        if (header.length() > 10 && header.substring(1, 10).startsWith("#!SILK_V3")) {
            return "audio/silk";
        }
        if (header.startsWith("#!AMR")) {
            return " audio/amr";
        }

        // Check binary types
        if (header.startsWith("PK")) {
            return "application/zip";
        }
        if (header.startsWith("GI")) {
            return "image/gif";
        }

        // Check text types
        String textHeader = header.trim().toUpperCase(Locale.US);
        if (textHeader.startsWith("<!DOCTYPE HTML") || textHeader.startsWith("<HTML") || textHeader.startsWith("<HEAD")
                || textHeader.startsWith("<BODY") || textHeader.startsWith("<HEAD")) {
            return "text/html";
        }

        if (textHeader.startsWith("<?XML")) {
            return "application/xml";
        }

        // Give up
        return null;
    }

    public static void streamCopy(InputStream input, OutputStream output)
            throws IOException {
        // Is there really no built-in for this?

        try {
            final byte[] buffer = new byte[8192];
            int n;

            while (true) {
                n = input.read(buffer, 0, buffer.length);
                if (n < 0) {
                    break;
                }
                output.write(buffer, 0, n);
            }

        } finally {
            input.close();
            output.close();
        }
    }

    /**
     * @param source amr文件
     * @param target wav文件
     */
    public static void amr2wav(File source, final File target) {
        String tmpdir = System.getProperty("java.io.tmpdir");
        if (tmpdir == null || tmpdir.trim().isEmpty()) {
            tmpdir = "C:\\Windows\\Temp";
        }
        final File folder = new File(tmpdir, "amr2wav");
        if (!folder.isDirectory()) {
            folder.mkdir();
        }
        File decoderFile = new File(folder, "silk_v3_decoder.exe");
        if (!decoderFile.isFile()) {
            InputStream inputStream = Amr2Wav.class.getResourceAsStream("/" + decoderFile.getName());
            if (inputStream != null) {
                try {
                    streamCopy(inputStream, new FileOutputStream(decoderFile));
                } catch (IOException e) {
                    System.err.println("复制exe文件出错:" + e.getMessage());
                }
            }
        }
        if (!decoderFile.isFile()) {
            System.err.println("not found:" + decoderFile.getAbsolutePath());
            return;
        }
        final File tmpFile = new File(folder, target.getName());
        /*解决路径空格 被终端截断的问题*/
        final String cmd = "\""+decoderFile.getAbsolutePath() + "\"  \"" + source.getAbsolutePath() + "\"  \"" + tmpFile.getAbsolutePath()+"\"";
        Spawn.startProcess(cmd, folder, new ProcessListener() {

            @Override
            public void onOutputLine(final String line) {
                System.out.println(line);
            }


            @Override
            public void onErrorLine(final String line) {

            }

            @Override
            public void onProcessQuit(int returnValue) {
                // System.out.println("::" + returnValue);
                try {
                    if (tmpFile.isFile()) {
                        convertAudioFiles(tmpFile.getAbsolutePath(), target.getAbsolutePath());
                        if (target.isFile()) {
                            tmpFile.deleteOnExit();
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onError(final Throwable t) {
                throw new RuntimeException(t);
            }
        });
    }

    public static void main(String[] args) throws Exception {
        File source = new File("D:\\xorm\\msg_3217160526162130e57c902104.amr");// new File("D:\\xorm\\silk2mp3\\tet.pcm");
        File target = new File("D:\\xorm\\silk2mp3\\2104.wav");

        Amr2Wav.amr2wav(source, target);


//        Encoder encoder = new Encoder();
//        AudioAttributes audio = new AudioAttributes();
//       audio.setCodec("s16le");
//       // audio.setCodec("pcm_u8");
//        audio.setChannels(2);
//        audio.setBitRate(1411);
//        EncodingAttributes attrs = new EncodingAttributes();
//        attrs.setFormat("wav");
//        attrs.setAudioAttributes(audio);
//        encoder.encode(source, target, attrs);
        //   MultimediaInfo info = encoder.getInfo(source);
        //   System.out.println("duration:" + info.getDuration());

        //   System.out.println("format:" + info.getFormat());
        //    AudioInfo audioInfo = info.getAudio();
        //    System.out.println("audio decoder:" + audioInfo.getDecoder());
    }

//    public static void mp42weixin(String[] args) throws EncoderException {
//        File source = new File("C:\\Users\\vps\\Downloads\\qCC408GAsmPC~CIc-5G8iQ__.mp4");
//        File aa = new File("C:\\Users\\vps\\Downloads\\qCC408GAsmPC~CIc-5G8iQ__.compressed.mp4");
//        File target = new File("D:\\DbTools_log\\target.mp4");
//        File test = new File("D:\\DbTools_log\\sight_12269310082450460887");
//        if (true) {
//            String abc = null;
//            try {
//                abc = guessContentTypeFromStream(new BufferedInputStream(new FileInputStream(aa)));
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
//            System.out.println(abc);
//            return;
//        }
//        Encoder encoder = new Encoder();
//
//        MultimediaInfo info = encoder.getInfo(source);
//        System.out.println("duration:" + info.getDuration());
//
//        System.out.println("format:" + info.getFormat());
//
//        VideoInfo videoInfo = info.getVideo();
//
//        System.out.println("video decoder:" + videoInfo.getDecoder());
//        AudioInfo audioInfo = info.getAudio();
//        System.out.println("audio decoder:" + audioInfo.getDecoder());
//
//
//        AudioAttributes audio = new AudioAttributes();
//        audio.setCodec("aac");
//        //   audio.setCodec("libfaac");
//        //  audio.setCodec(AudioAttributes.DIRECT_STREAM_COPY);
//        audio.setBitRate(new Integer(64000));
//        audio.setSamplingRate(new Integer(44100));
//        audio.setChannels(new Integer(2));
//
//        VideoAttributes video = new VideoAttributes();
//        video.setCodec("libx264");
//        video.setBitRate(new Integer(160000));
//        video.setFrameRate(new Integer(15));
//        video.setSize(new VideoSize(256, 144));
//        video.setX264Profile(VideoAttributes.X264_PROFILE.MAIN);
//
//        EncodingAttributes attrs = new EncodingAttributes();
//        attrs.setFormat("mp4");
//
//
//        attrs.setAudioAttributes(audio);
//        attrs.setVideoAttributes(video);
//        attrs.setDuration(15.0f);
//        attrs.setOffset(0.0f);
//
//
//        encoder.encode(source, target, attrs, new EncoderProgressListener() {
//            @Override
//            public void sourceInfo(MultimediaInfo multimediaInfo) {
//                System.out.println("duration:" + multimediaInfo.getDuration());
//            }
//
//            @Override
//            public void progress(int i) {
//                System.out.println("progress:" + i);
//            }
//
//            @Override
//            public void message(String s) {
//                System.out.println("info:" + s);
//            }
//        });
////        info = encoder.getInfo(target);
////        System.out.println("duration:" + info.getDuration());
////
////        System.out.println("format:" + info.getFormat());
////
////        videoInfo = info.getVideo();
////        System.out.println("decoder:" + videoInfo.getDecoder());
////        audioInfo = info.getAudio();
////        System.out.println("audio decoder:" + audioInfo.getDecoder());
//        /*
//        format:mov
//video decoder:h264 (Main) (avc1 / 0x31637661)
//audio decoder:aac (mp4a / 0x6134706D)
//
//        * format:mov
//video decoder:h264 (High) (avc1 / 0x31637661)
//audio decoder:aac (mp4a / 0x6134706D)
//        *
//        * format:mov
//decoder:h264 (High) (avc1 / 0x31637661)
//audio decoder:aac (mp4a / 0x6134706D)
//        * */
//    }

    public static void convertAudioFiles(String src, String target) throws Exception {
        FileInputStream fis = new FileInputStream(src);
        FileOutputStream fos = new FileOutputStream(target);

        // 计算长度
        byte[] buf = new byte[1024 * 4];
        int size = fis.read(buf);
        int PCMSize = 0;
        while (size != -1) {
            PCMSize += size;
            size = fis.read(buf);
        }
        fis.close();

        // 填入参数，比特率等等。这里用的是16位单声道 8000 hz
        WaveHeader header = new WaveHeader();
        // 长度字段 = 内容的大小（PCMSize) + 头部字段的大小(不包括前面4字节的标识符RIFF以及fileLength本身的4字节)
        header.fileLength = PCMSize + (44 - 8);
        header.FmtHdrLeth = 16;
        header.BitsPerSample = 16;
        header.Channels = 1;
        header.FormatTag = 0x0001;
        // header.SamplesPerSec = 18000;
        header.SamplesPerSec = 24000;
        header.BlockAlign = (short) (header.Channels * header.BitsPerSample / 8);
        header.AvgBytesPerSec = header.BlockAlign * header.SamplesPerSec;
        header.DataHdrLeth = PCMSize;

        byte[] h = header.getHeader();

        assert h.length == 44; // WAV标准，头部应该是44字节
        // write header
        fos.write(h, 0, h.length);
        // write data stream
        fis = new FileInputStream(src);
        size = fis.read(buf);
        while (size != -1) {
            fos.write(buf, 0, size);
            size = fis.read(buf);
        }
        fis.close();
        fos.close();
        System.out.println("Convert OK!");
    }
}
